﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ImageDownloadClient.Properties;
using FlyingAPI;
using System.IO;
using System.Net;

namespace ImageDownloadClient
{
    class ImageDownloaderController
    {
        private static int[] can = new int[5];


        public enum ImageDownloaderStatus { STATUS_STOP, STATUS_PAUSE, STATUS_WORKING };

        private static ImageDownloadClient.ImageDownloaderController instance;
        private int producerNum;
        private int consumerNum;

        private LogAppend WritelnLog;
        private FlyingPig flyingPig;
        private String concept;
        private String downloadFolder;

        public String Concept
        {
            get { return concept; }
            set { concept = value; }
        }
        public String DownloadFolder
        {
            get { return downloadFolder; }
            set { downloadFolder = value; }
        }

        public FlyingPig FlyingPig
        {
            get { return flyingPig; }
            set { flyingPig = value; }
        }

        private ConsumeAndProduceFactory.ConsumeAndProduceFactory factory;

        public ConsumeAndProduceFactory.ConsumeAndProduceFactory Factory
        {
            get { return factory; }
            set { factory = value; }
        }

        private ImageDownloaderController(LogAppend g)
        {
            WritelnLog = g;
            flyingPig = new FlyingPig();
            // 初始化状态
            m_DownloadStatus = ImageDownloaderStatus.STATUS_STOP;
            producerNum = Settings.Default.producerNum;
            consumerNum = Settings.Default.consumerNum;
           
            // 初始化factory
            factory = new ConsumeAndProduceFactory.ConsumeAndProduceFactory(producerNum, consumerNum, 50);
            factory.Consuming += new ConsumeAndProduceFactory.FactoryEventHandler(this.uploadImage);
            factory.Producing += new ConsumeAndProduceFactory.FactoryEventHandler(this.requestAndDownloadImage);
            factory.OnceConsume = 5;
        }

        public static ImageDownloaderController getInstance(LogAppend g)
        {
            if (instance == null)
            {
                instance = new ImageDownloaderController(g);
            }

            return instance;
        }

        public void start()
        {
            // 启动生产者消费者模型
            int a = factory.startwork();
            if (a == 0)
                WritelnLog("开始工作");
            else
                WritelnLog("错误，无法启动生产者");

            // 启动完factory后，设置状态
            m_DownloadStatus = ImageDownloaderStatus.STATUS_WORKING;
        }

        public void pause()
        {
            // 暂停factory
            factory.pausework();

            // 暂停完factory后，设置状态
            m_DownloadStatus = ImageDownloaderStatus.STATUS_PAUSE;
        }

        public void stop()
        {
            // 停止factory
            factory.stopwork();

            // 停止完factory后，设置状态
            m_DownloadStatus = ImageDownloaderStatus.STATUS_STOP;
        }

        public void resume()
        {
            // 恢复
            factory.resumework();

            // 启动完factory后，设置状态
            m_DownloadStatus = ImageDownloaderStatus.STATUS_WORKING;
        }

        public ImageDownloaderStatus getCurStatus()
        {
            return m_DownloadStatus;
        }

        private ImageDownloaderStatus m_DownloadStatus;

        // 生产
        public void requestAndDownloadImage(object sender, ConsumeAndProduceFactory.FactoryEventArgs e)
        {
            int count = 0;
            try
            {
                List<ImageInfo> imageinfos = flyingPig.requestImagesInfos(20, this.concept);

                if (imageinfos != null)
                {
                    if (imageinfos.Count > 0)
                    {
                        WritelnLog("请求到了" + imageinfos.Count + "个ImageInfo");
                        foreach (ImageInfo ii in imageinfos)
                        {
                            //最多尝试5次下载，若不能下载，返回失败
                            int repeat = 0;
                            PhotoBuffer result = null;
                            long nsize = -1;

                            while (repeat < 5 && nsize == -1)
                            {
                                result = downloadFile(ii, this.downloadFolder +"\\"+ ii.Concept);
                                if (result.data != null)
                                    nsize = result.data.Length;
                                else nsize = -1;
                            }

                            if (nsize != -1)
                            {
                                count++;
                                //WritelnLog("从"+ii.Url+"下载图片成功，传输了"+nsize+"个字节");
                                
                                e.Products.Add(result);
                            }
                            //else 若出错，在DownloadFile的Catch中会做处理
                        }
                        e.Msg = ConsumeAndProduceFactory.FactoryMsg.ProducingOk;
                        WritelnLog("此次生产共下载了"+count+"/"+imageinfos.Count+"张图片");
                    }
                    else
                        e.Msg = ConsumeAndProduceFactory.FactoryMsg.ProducingWithError;
                }
                else
                {
                    WritelnLog("请求结果为空，该线程的生产完成");
                    e.Msg = ConsumeAndProduceFactory.FactoryMsg.ProducingFinished;
                    e.Products = null;
                }
            }
            catch(Exception ex)
            {
                WritelnLog("=============Download Image Error==============");
                WritelnLog(ex.Message);
                WritelnLog("===============================================");
            }
        }

        //由ID获得其应下载到的子文件夹
        public String getSubDirectoryByPhotoId(int id)
        {
            const int SEGMENT = 10000;
            try
            {
                int count = id / SEGMENT;
                int offset = id % SEGMENT;

                StringBuilder buffer = new StringBuilder();

                if (count == 0) buffer.Append("\\0-9999\\");
                else buffer.Append(count).Append("\\0000").Append(count).Append("9999\\");

                return buffer.ToString();
            }
            catch (Exception)
            {
                return "\\Exception";
            }
        }

        //使用简单的断点续传手法下载文件
        private PhotoBuffer downloadFile(ImageInfo ii, String destDir)
        {
            String url = ii.Url;
            int imageID = ii.ImageId;
            String ext =  ii.Url.Substring(ii.Url.LastIndexOf('.'));

            FileStream m_fileWriter = null;
            Stream reader = null;
            byte[] buff = new byte[0];
            long FinishedSize = 0;

            destDir = destDir + getSubDirectoryByPhotoId(imageID);
            String fileName = imageID.ToString();

            String fullName = destDir + fileName + ext;

            try
            {
                // 创建文件夹
                if (!Directory.Exists(destDir))
                {
                    Directory.CreateDirectory(destDir);
                }
                //查看是否已存在文件，如果是，就需要断点续传
                long lStartPos = 0;
                if (System.IO.File.Exists(fullName))
                {
                    m_fileWriter = System.IO.File.OpenWrite(fullName);
                    lStartPos = m_fileWriter.Length;
                    m_fileWriter.Close();
                }
                else
                {
                    lStartPos = 0;
                }

                //打开网络连接 
                System.Net.HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
                request.Timeout = 15000;
                WebResponse response = request.GetResponse();

                long fileLength = response.ContentLength;


                if (lStartPos < fileLength) //如果未完成下载
                {
                    if (lStartPos > 0)
                        request.AddRange((int)lStartPos); //设置Range值 

                    //向服务器请求，获得服务器回应数据流 
                    reader = response.GetResponseStream();

                    buff = new byte[fileLength];
                    int justRead = 0;                         // 刚读取的字节数
                    FinishedSize = 0;                         // 至今读取的字节数
                    while ((justRead = reader.Read(buff, 0, buff.Length)) > 0)
                    {
                        //不直接写文件，而是消费时再写
                        FinishedSize += justRead;
                    }
                    reader.Close();
                }

                //返回已读字节数
                return new PhotoBuffer(ii,fullName,buff);
            }
            catch (System.Exception ex)
            {
                if (reader != null) reader.Close();
                if (m_fileWriter != null) m_fileWriter.Close();
                WritelnLog("=============Download Image Error==============");
                WritelnLog(ex.Message);
                WritelnLog("===============================================");
                return null;
            }
        }

        // 消费
        public void uploadImage(object sender, ConsumeAndProduceFactory.FactoryEventArgs e)
        {
            int count = 0;
            if (e.Products == null) return;

            foreach (object ob in e.Products)
            {
                count++;            

                //将数据写入硬盘
                writeToFile(((PhotoBuffer)ob).fileName, ((PhotoBuffer)ob).data);

                
                //经由API调用将ImageInfo上传至服务器
                
/*                FlyingPigSTATUS status = FlyingPig.uploadImageInfo(ii);
                if (count % 100 == 0)
                {
                    WritelnLog(System.Threading.Thread.CurrentThread.Name + " ||| " + " pageID is " + ii.PageId + ", count is " + count.ToString() + " ||| " +status.ToString());
                }
                if (status.Equals(FlyingPigSTATUS.SERVER_INNER_FAILED))
                {
                    break;
                }*/
            }
            WritelnLog("消费了"+count+"/"+e.Products.Count+"张图片...");
            System.GC.Collect();
//            WritelnLog("Upload Done ||| count is " + count.ToString());
        }

        public void writeToFile(String fullfileName, byte[] data)
        {
            FileStream m_fileWriter = null;
            int fileLength = data.Length;

            try
            {
                long lStartPos = 0;
                if (System.IO.File.Exists(fullfileName))
                {
                    m_fileWriter = System.IO.File.OpenWrite(fullfileName);
                    lStartPos = m_fileWriter.Length;
                    m_fileWriter.Seek(lStartPos, System.IO.SeekOrigin.Current); //移动文件流中的当前指针 
                }
                else
                {
                    m_fileWriter = new System.IO.FileStream(fullfileName, System.IO.FileMode.Create, FileAccess.Write);
                    lStartPos = 0;
                }


                if (lStartPos < fileLength) //如果未完成下载
                {
                    m_fileWriter.Write(data, 0, fileLength);
                    m_fileWriter.Close();
                }
            }
            catch (Exception ex)
            {
                if (m_fileWriter != null) m_fileWriter.Close();
                WritelnLog("================Image Write Error==============");
                WritelnLog("File Name:" + fullfileName);
                WritelnLog(ex.Message);
                WritelnLog("===============================================");
            }
        }
    }
}
